package com.hxx.widget.richtext.editor

import android.content.Context
import android.text.*
import android.text.style.CharacterStyle
import android.text.style.ParagraphStyle
import android.util.AttributeSet
import android.util.Log
import android.view.KeyEvent
import androidx.appcompat.widget.AppCompatEditText
import androidx.core.text.getSpans
import com.hxx.widget.richtext.SpanFactory
import com.hxx.widget.richtext.TextUtils
import com.hxx.widget.richtext.toolbar.RichTextStyleMenu
import com.hxx.widget.richtext.views.NumberBulletSpan

class EditorEditText @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatEditText(context, attrs, defStyleAttr), Editor {
    private val TAG = "文本编辑"
    private val saver = EditorSaveStater.getSaver().apply {
        saveNode(EditorCharHistory())
        setStateChangedListener(object : EditorSaveStater.OnStateChangedListener {
            override fun onChanged() {
                Log.i(TAG, "onChanged: 变动")
            }
        })
    }
    private val textWatcher = object : TextWatcher {
        private lateinit var history: EditorCharHistory
        private var start = 0
        private var end = 0
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            Log.i(TAG, "beforeTextChanged: $s,$start,$count,$after")
            history = EditorCharHistory()
            history.oldChars = s?.subSequence(start, start + count).toString()
            val characterSpans = text!!.getSpans(0, text!!.length, CharacterStyle::class.java)
            val paragraphSpans = text!!.getSpans(0, text!!.length, ParagraphStyle::class.java)
            val allOldSpans = arrayListOf<StepSpanInfo>()
            characterSpans.forEach {
                val item = StepSpanInfo(it, text!!.getSpanStart(it), text!!.getSpanEnd(it))
                allOldSpans.add(item)
            }
            paragraphSpans.forEach {
                val item = StepSpanInfo(it, text!!.getSpanStart(it), text!!.getSpanEnd(it))
                allOldSpans.add(item)
            }
            history.oldSpans = allOldSpans
        }

        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
            Log.i(TAG, "onTextChanged: $s,$start,$before,$count")
            history.replaceChars = s.subSequence(start, start + count).toString()
            history.start = start
            this.start = start
            this.end = count + start
            for (i in start until start + count) {
                if (s[i] == '\n') {
                    end = i
                    break
                }
            }
        }

        override fun afterTextChanged(s: Editable) {
            Log.i(TAG, "afterTextChanged: 文本编辑变动:$history")

            if (menu?.shouldUpdateStyle(s, start, end) == true) {
                TextUtils.splitSpans(s, start, end)
                if (start != end) {
                    menu!!.getCharacterStyle().forEach {
                        text!!.setSpan(
                            it,
                            start,
                            end,
                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                        )
                    }
                }
            }
            //段落分割 换行符分割
            s.getSpans(start, end, ParagraphStyle::class.java).forEach {
                val spanStart = s.getSpanStart(it)
                val newSpanEnd = s.getSpanEnd(it)
                var splitSpanStart = spanStart
                for (i in spanStart..newSpanEnd) {
                    if (i == newSpanEnd) {
                        //到结尾了
                        if (splitSpanStart < i) {
                            val span = SpanFactory.copySpan(it)
                            s.setSpan(span, splitSpanStart, i, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                        }
                        break
                    }
                    if (s[i] == '\n') {
                        //当前字符为换行符，段落进行分割
                        if (splitSpanStart < i) {
                            val span = SpanFactory.copySpan(it)
                            s.setSpan(span, splitSpanStart, i, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                        }
                        splitSpanStart = i + 1
                    }
                }
                s.removeSpan(it)
            }
            val characterSpans = s.getSpans(0, s.length, CharacterStyle::class.java)
            val paragraphSpans = s.getSpans(0, s.length, ParagraphStyle::class.java)
            val allOldSpans = arrayListOf<StepSpanInfo>()
            characterSpans.forEach {
                val item = StepSpanInfo(it, s.getSpanStart(it), s.getSpanEnd(it))
                allOldSpans.add(item)
            }
            paragraphSpans.forEach {
                val item = StepSpanInfo(it, s.getSpanStart(it), s.getSpanEnd(it))
                allOldSpans.add(item)
            }
            history.currentSpans = allOldSpans
            saver.saveNode(history)
        }
    }
    private var menu: RichTextStyleMenu? = null

    /**
     * 需要即时生效的样式
     */
    private fun runInRange(block: (Int, Int) -> Unit) {
        val s = Selection.getSelectionStart(text)
        val e = Selection.getSelectionEnd(text)
        if (s != e) {
            runHistoryBlock {
                block(s, e)
            }
        }
    }

    fun bindMenu(menu: RichTextStyleMenu) {
        this.menu = menu
        menu.addStyleListener(object : RichTextStyleMenu.StyleListener {
            override fun onBoldChanged(enable: Boolean) {
                runInRange { s, e ->
                    TextUtils.splitSpans(text!!, s, e)
                    menu.getCharacterStyle().forEach {
                        text!!.setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                    }
                }
            }

            override fun onItalicChanged(enable: Boolean) {
                runInRange { s, e ->
                    TextUtils.splitSpans(text!!, s, e)
                    menu.getCharacterStyle().forEach {
                        text!!.setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                    }
                }
            }

            override fun onUnderlineChanged(enable: Boolean) {
                runInRange { s, e ->
                    TextUtils.splitSpans(text!!, s, e)
                    menu.getCharacterStyle().forEach {
                        text!!.setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                    }
                }
            }

            override fun onStrikeThroughChanged(enable: Boolean) {
                runInRange { s, e ->
                    TextUtils.splitSpans(text!!, s, e)
                    menu.getCharacterStyle().forEach {
                        text!!.setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                    }
                }
            }

            override fun onListOrderChanged(enable: Boolean) {
                val s = Selection.getSelectionStart(text)
                val e = Selection.getSelectionEnd(text)
                runHistoryBlock {
                    var ps = s
                    var pe = e
                    for (i in Math.max(0, s - 1) downTo 0) {
                        if (text!![i] == '\n') {
                            ps = Math.min(i + 1, s)
                            break
                        }
                        if (i == 0) {
                            ps = 0
                        }
                    }
                    for (i in e..text!!.length) {
                        if (i == text!!.length) {
                            pe = text!!.length
                            break
                        }
                        if (text!![i] == '\n') {
                            pe = i
                            break
                        }
                    }
                    val listSpans = text!!.getSpans<NumberBulletSpan>(ps, pe)
                    listSpans.forEach {
                        text!!.removeSpan(it)
                    }
                    if (enable) {
                        var lineStart = ps
                        for (i in ps..pe) {
                            if (i == pe) {
                                //段落结束
                                if (lineStart < i) {
                                    val newSpan = NumberBulletSpan(1)
                                    text!!.setSpan(
                                        newSpan,
                                        lineStart,
                                        i,
                                        Spanned.SPAN_EXCLUSIVE_INCLUSIVE
                                    )
                                }
                                break
                            }
                            val char = text!![i]
                            if (char == '\n') {
                                val newSpan = NumberBulletSpan(1)
                                text!!.setSpan(
                                    newSpan,
                                    lineStart,
                                    i,
                                    Spanned.SPAN_EXCLUSIVE_INCLUSIVE
                                )
                                lineStart = i + 1
                            }
                        }
                    }
                }
            }

            override fun onAlignmentChanged(alignment: Layout.Alignment) {
                //整体思路
                /**
                 * 查询影响范围段落
                 * 每个段落第一个字符使用 \u200B 占用
                 * 首个不可见字符直接设置样式
                 * span
                 */

            }

            override fun onColorChanged(color: Int) {
                runInRange { s, e ->
                    TextUtils.splitSpans(text!!, s, e)
                    menu.getCharacterStyle().forEach {
                        text!!.setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                    }
                }
            }

            override fun onFontSizeChanged(scale: Float) {
                runInRange { s, e ->
                    TextUtils.splitSpans(text!!, s, e)
                    menu.getCharacterStyle().forEach {
                        text!!.setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                    }
                }
            }
        })
    }


    private fun runHistoryBlock(block: EditorCharHistory.() -> Unit) {
        val history = EditorCharHistory()
        history.oldSpans = saveSpans()
        history.start = Selection.getSelectionStart(text)
        runNoWatcherBlock {
            history.block()
        }
        history.currentSpans = saveSpans()
        saver.saveNode(history)
    }

    private fun saveSpans(): List<StepSpanInfo> {
        val characterSpans = text!!.getSpans(0, text!!.length, CharacterStyle::class.java)
        val paragraphSpans = text!!.getSpans(0, text!!.length, ParagraphStyle::class.java)
        val allOldSpans = arrayListOf<StepSpanInfo>()
        characterSpans.forEach {
            val item = StepSpanInfo(it, text!!.getSpanStart(it), text!!.getSpanEnd(it))
            allOldSpans.add(item)
        }
        paragraphSpans.forEach {
            val item = StepSpanInfo(it, text!!.getSpanStart(it), text!!.getSpanEnd(it))
            allOldSpans.add(item)
        }
        return allOldSpans
    }

    override fun onSelectionChanged(selStart: Int, selEnd: Int) {
        super.onSelectionChanged(selStart, selEnd)
        menu?.onSelectionChanged(text!!)
        Log.i(TAG, "onSelectionChanged: $selStart")
        text?.apply {
            getSpans<CharacterStyle>(0, length).forEach {
                val s = getSpanStart(it)
                val e = getSpanEnd(it)
                if (getSpanFlags(it) != Spanned.SPAN_EXCLUSIVE_EXCLUSIVE && e != selStart) {
                    removeSpan(it)
                    setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                }
                if (e == selStart && getSpanFlags(it) != Spanned.SPAN_EXCLUSIVE_INCLUSIVE) {
                    removeSpan(it)
                    setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                }
            }

            getSpans<ParagraphStyle>(0, length).forEach {
                val s = getSpanStart(it)
                val e = getSpanEnd(it)
                if (getSpanFlags(it) != Spanned.SPAN_EXCLUSIVE_EXCLUSIVE && e != selStart) {
                    removeSpan(it)
                    setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                }
                if (e == selStart && getSpanFlags(it) != Spanned.SPAN_EXCLUSIVE_INCLUSIVE) {
                    removeSpan(it)
                    setSpan(it, s, e, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
                }
            }
        }

    }

    private val spanWatcher = object : SpanWatcher {
        private fun updateParagraphSpansRange(text: Spannable, what: Any) {
            val spanStart = text.getSpanStart(what)
            val spanEnd = text.getSpanEnd(what)
            var newSpanEnd = spanEnd
            if (spanStart != 0 && text[spanStart - 1] != '\n') {
                text.removeSpan(what)
                return
            }
            if (spanEnd != text.length && text[spanEnd] != '\n') {
                for (i in spanEnd..text.length) {
                    if (i == text.length) {
                        newSpanEnd = i
                        break
                    }
                    if (text[i] == '\n') {
                        newSpanEnd = i
                        break
                    }
                }
                text.removeSpan(what)
                text.setSpan(what, spanStart, newSpanEnd, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
            }
        }

        override fun onSpanAdded(text: Spannable, what: Any, start: Int, end: Int) {
            if (what is ParagraphStyle) {
                Log.i(TAG, "onSpanAdded: 添加span:$what,$start,$end")
                updateParagraphSpansRange(text, what)
            }
        }

        override fun onSpanRemoved(text: Spannable?, what: Any?, start: Int, end: Int) {

        }

        override fun onSpanChanged(
            text: Spannable,
            what: Any?,
            ostart: Int,
            oend: Int,
            nstart: Int,
            nend: Int
        ) {
            if (what is ParagraphStyle) {
                updateParagraphSpansRange(text, what)
                Log.i(TAG, "onSpanChanged: $what,$nstart,$nend")
                if (nstart == nend || (nstart != 0 && text[nstart - 1] != '\n')) {
                    text.removeSpan(what)
                }
            }
        }
    }

    init {
        setText(SpannableStringBuilder(""), BufferType.EDITABLE)
        text?.apply {
            setSpan(spanWatcher, 0, length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
        }
        addTextChangedListener(textWatcher)
        filters = arrayOf(object : InputFilter {
            override fun filter(
                source: CharSequence,
                start: Int,
                end: Int,
                dest: Spanned,
                dstart: Int,
                dend: Int
            ): CharSequence {
                //将每个换行符都拼接不可见字符 \u200B,不可见字符设置段落样式
                val charBuilder = StringBuffer("")
                for (i in start until end) {
                    if (source[i] == '\n') {
                        charBuilder.append('\n')
                        charBuilder.append(TextUtils.REPLACE_CHAR)
                    } else {
                        charBuilder.append(source[i])
                    }
                }
                return charBuilder
            }
        })
        setOnKeyListener { v, keyCode, event ->
            Log.i(TAG, "code: $keyCode")
            if (keyCode == KeyEvent.KEYCODE_DEL) {
                val selectionStart = Selection.getSelectionStart(text)
                val selectionEnd = Selection.getSelectionEnd(text)
                if (selectionStart == selectionEnd) {
                    val pspans = text!!.getSpans<ParagraphStyle>(selectionStart, selectionEnd)
                    pspans.forEach {
                        val start = text!!.getSpanStart(it)
                        if (start == selectionStart) {
                            text!!.removeSpan(it)
                        }
                    }
                }
            }
            return@setOnKeyListener false
        }
    }

    override fun setText(text: CharSequence?, type: BufferType?) {
        super.setText(text, type)
    }

    override fun undo() {
        val history = saver.undoNode() as EditorCharHistory
        Log.i(TAG, "undo: $history")
        runNoWatcherBlock {
            history.undo(text!!)
        }
    }

    private fun runNoWatcherBlock(block: () -> Unit) {
        removeTextChangedListener(textWatcher)
        block()
        addTextChangedListener(textWatcher)
    }

    override fun redo() {
        val history = saver.redoNode() as EditorCharHistory
        Log.i(TAG, "redo: $history")
        runNoWatcherBlock {
            history.redo(text!!)
        }
    }

    override fun canUndo(): Boolean {
        return saver.canUndo()
    }

    override fun canRedo(): Boolean {
        return saver.canRedo()
    }

    override fun save() {

    }
}